import { CurrencyAmount, Ether, Native, Percent, Token, TradeType, WETH9 } from '@pancakeswap/sdk'
import { describe, it, expect } from 'vitest'
import { FeeAmount, TICK_SPACINGS } from './constants'
import { Pool } from './entities/pool'
import { SwapRouter } from './swapRouter'
import { nearestUsableTick, TickMath } from './utils'
import { encodeSqrtRatioX96 } from './utils/encodeSqrtRatioX96'
import { Route, Trade } from './entities'

describe('SwapRouter', () => {
  const ETHER = Native.onChain(1)
  const token0 = new Token(1, '0x0000000000000000000000000000000000000001', 18, 't0', 'token0')
  const token1 = new Token(1, '0x0000000000000000000000000000000000000002', 18, 't1', 'token1')
  const token2 = new Token(1, '0x0000000000000000000000000000000000000003', 18, 't2', 'token2')
  const token3 = new Token(1, '0x0000000000000000000000000000000000000004', 18, 't3', 'token3')

  const feeAmount = FeeAmount.MEDIUM
  const sqrtRatioX96 = encodeSqrtRatioX96(1, 1)
  const liquidity = 1_000_000
  const WETH = WETH9[1]

  const makePool = (token0: Token, token1: Token) => {
    return new Pool(token0, token1, feeAmount, sqrtRatioX96, liquidity, TickMath.getTickAtSqrtRatio(sqrtRatioX96), [
      {
        index: nearestUsableTick(TickMath.MIN_TICK, TICK_SPACINGS[feeAmount]),
        liquidityNet: liquidity,
        liquidityGross: liquidity,
      },
      {
        index: nearestUsableTick(TickMath.MAX_TICK, TICK_SPACINGS[feeAmount]),
        liquidityNet: -liquidity,
        liquidityGross: liquidity,
      },
    ])
  }

  const pool_0_1 = makePool(token0, token1)
  const pool_1_weth = makePool(token1, WETH)
  const pool_0_2 = makePool(token0, token2)
  const pool_0_3 = makePool(token0, token3)
  const pool_2_3 = makePool(token2, token3)
  const pool_3_weth = makePool(token3, WETH)
  const pool_1_3 = makePool(token3, token1)

  const slippageTolerance = new Percent(1, 100)
  const recipient = '0x0000000000000000000000000000000000000003'
  const deadline = 123

  describe('#swapCallParameters', () => {
    describe('single trade input', () => {
      it('single-hop exact input', async () => {
        const trade = await Trade.fromRoute(
          new Route([pool_0_1], token0, token1),
          CurrencyAmount.fromRawAmount(token0, 100),
          TradeType.EXACT_INPUT
        )
        const { calldata, value } = SwapRouter.swapCallParameters(trade, {
          slippageTolerance,
          recipient,
          deadline,
        })

        expect(calldata).toBe(
          '0x414bf3890000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000009c40000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007b000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000610000000000000000000000000000000000000000000000000000000000000000'
        )
        expect(value).toBe('0x00')
      })

      it('single-hop exact output', async () => {
        const trade = await Trade.fromRoute(
          new Route([pool_0_1], token0, token1),
          CurrencyAmount.fromRawAmount(token1, 100),
          TradeType.EXACT_OUTPUT
        )
        const { calldata, value } = SwapRouter.swapCallParameters(trade, {
          slippageTolerance,
          recipient,
          deadline,
        })

        expect(calldata).toBe(
          '0xdb3e21980000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000009c40000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007b000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000670000000000000000000000000000000000000000000000000000000000000000'
        )
        expect(value).toBe('0x00')
      })

      it('multi-hop exact input', async () => {
        const trade = await Trade.fromRoute(
          new Route([pool_0_1, pool_1_weth], token0, WETH),
          CurrencyAmount.fromRawAmount(token0, 100),
          TradeType.EXACT_INPUT
        )
        const { calldata, value } = SwapRouter.swapCallParameters(trade, {
          slippageTolerance,
          recipient,
          deadline,
        })

        expect(calldata).toBe(
          '0xc04b8d59000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007b0000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000005f000000000000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000010009c400000000000000000000000000000000000000020009c4c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000'
        )
        expect(value).toBe('0x00')
      })

      it('multi-hop exact output', async () => {
        const trade = await Trade.fromRoute(
          new Route([pool_0_1, pool_1_weth], token0, WETH),
          CurrencyAmount.fromRawAmount(WETH, 100),
          TradeType.EXACT_OUTPUT
        )
        const { calldata, value } = SwapRouter.swapCallParameters(trade, {
          slippageTolerance,
          recipient,
          deadline,
        })

        expect(calldata).toBe(
          '0xf28c0498000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007b000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000690000000000000000000000000000000000000000000000000000000000000042c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20009c400000000000000000000000000000000000000020009c40000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000'
        )
        expect(value).toBe('0x00')
      })

      it('ETH in exact input', async () => {
        const trade = await Trade.fromRoute(
          new Route([pool_1_weth], ETHER, token1),
          CurrencyAmount.fromRawAmount(Native.onChain(1), 100),
          TradeType.EXACT_INPUT
        )
        const { calldata, value } = SwapRouter.swapCallParameters(trade, {
          slippageTolerance,
          recipient,
          deadline,
        })

        expect(calldata).toBe(
          '0x414bf389000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000009c40000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007b000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000610000000000000000000000000000000000000000000000000000000000000000'
        )
        expect(value).toBe('0x64')
      })

      it('ETH in exact output', async () => {
        const trade = await Trade.fromRoute(
          new Route([pool_1_weth], ETHER, token1),
          CurrencyAmount.fromRawAmount(token1, 100),
          TradeType.EXACT_OUTPUT
        )
        const { calldata, value } = SwapRouter.swapCallParameters(trade, {
          slippageTolerance,
          recipient,
          deadline,
        })

        expect(calldata).toBe(
          '0xac9650d800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000104db3e2198000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000009c40000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007b00000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000067000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000412210e8a00000000000000000000000000000000000000000000000000000000'
        )
        expect(value).toBe('0x67')
      })

      it('ETH out exact input', async () => {
        const trade = await Trade.fromRoute(
          new Route([pool_1_weth], token1, ETHER),
          CurrencyAmount.fromRawAmount(token1, 100),
          TradeType.EXACT_INPUT
        )
        const { calldata, value } = SwapRouter.swapCallParameters(trade, {
          slippageTolerance,
          recipient,
          deadline,
        })

        expect(calldata).toBe(
          '0xac9650d800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000104414bf3890000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000000000000000000000000000000000000000009c40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b00000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000061000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004449404b7c0000000000000000000000000000000000000000000000000000000000000061000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000'
        )
        expect(value).toBe('0x00')
      })

      it('ETH out exact output', async () => {
        const trade = await Trade.fromRoute(
          new Route([pool_1_weth], token1, ETHER),
          CurrencyAmount.fromRawAmount(Native.onChain(1), 100),
          TradeType.EXACT_OUTPUT
        )
        const { calldata, value } = SwapRouter.swapCallParameters(trade, {
          slippageTolerance,
          recipient,
          deadline,
        })

        expect(calldata).toBe(
          '0xac9650d800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000104db3e21980000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000000000000000000000000000000000000000009c40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b00000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000067000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004449404b7c0000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000'
        )
        expect(value).toBe('0x00')
      })

      it('sqrtPriceLimitX96', async () => {
        const trade = await Trade.fromRoute(
          new Route([pool_0_1], token0, token1),
          CurrencyAmount.fromRawAmount(token0, 100),
          TradeType.EXACT_INPUT
        )
        const { calldata, value } = SwapRouter.swapCallParameters(trade, {
          slippageTolerance,
          recipient,
          deadline,
          sqrtPriceLimitX96: 2n ** 128n,
        })

        expect(calldata).toBe(
          '0x414bf3890000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000009c40000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007b000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000610000000000000000000000000000000100000000000000000000000000000000'
        )
        expect(value).toBe('0x00')
      })

      it('fee with eth out', async () => {
        const trade = await Trade.fromRoute(
          new Route([pool_1_weth], token1, ETHER),
          CurrencyAmount.fromRawAmount(token1, 100),
          TradeType.EXACT_INPUT
        )
        const { calldata, value } = SwapRouter.swapCallParameters(trade, {
          slippageTolerance,
          recipient,
          deadline,
          fee: {
            fee: new Percent(5, 1000),
            recipient,
          },
        })

        expect(calldata).toBe(
          '0xac9650d800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000104414bf3890000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000000000000000000000000000000000000000009c40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b0000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000006100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000849b2c0a37000000000000000000000000000000000000000000000000000000000000006100000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000'
        )
        expect(value).toBe('0x00')
      })

      it('fee with eth in using exact output', async () => {
        const trade = await Trade.fromRoute(
          new Route([pool_1_weth], ETHER, token1),
          CurrencyAmount.fromRawAmount(token1, 10),
          TradeType.EXACT_OUTPUT
        )
        const { calldata, value } = SwapRouter.swapCallParameters(trade, {
          slippageTolerance,
          recipient,
          deadline,
          fee: {
            fee: new Percent(5, 1000),
            recipient,
          },
        })

        expect(calldata).toBe(
          '0xac9650d800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000104db3e2198000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000009c40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a4e0e189a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000412210e8a00000000000000000000000000000000000000000000000000000000'
        )
        expect(value).toBe('0x0c')
      })

      it('fee', async () => {
        const trade = await Trade.fromRoute(
          new Route([pool_0_1], token0, token1),
          CurrencyAmount.fromRawAmount(token0, 100),
          TradeType.EXACT_INPUT
        )
        const { calldata, value } = SwapRouter.swapCallParameters(trade, {
          slippageTolerance,
          recipient,
          deadline,
          fee: {
            fee: new Percent(5, 1000),
            recipient,
          },
        })

        expect(calldata).toBe(
          '0xac9650d800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000104414bf3890000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000009c40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b0000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000006100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a4e0e189a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000006100000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000'
        )
        expect(value).toBe('0x00')
      })
    })
  })

  describe('multiple trade input', () => {
    it('two single-hop exact input', async () => {
      const trade1 = await Trade.fromRoute(
        new Route([pool_0_1], token0, token1),
        CurrencyAmount.fromRawAmount(token0, 100),
        TradeType.EXACT_INPUT
      )

      const trade2 = await Trade.fromRoute(
        new Route([pool_0_1], token0, token1),
        CurrencyAmount.fromRawAmount(token0, 100),
        TradeType.EXACT_INPUT
      )

      const { calldata, value } = SwapRouter.swapCallParameters([trade1, trade2], {
        slippageTolerance,
        recipient,
        deadline,
      })

      expect(calldata).toBe(
        '0xac9650d800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000104414bf3890000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000009c40000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007b000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000104414bf3890000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000009c40000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007b00000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000061000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
      )
      expect(value).toBe('0x00')
    })

    it('one single-hop one multi-hop exact input', async () => {
      const trade1 = await Trade.fromRoute(
        new Route([pool_0_3], token0, token3),
        CurrencyAmount.fromRawAmount(token0, 100),
        TradeType.EXACT_INPUT
      )

      const trade2 = await Trade.fromRoute(
        new Route([pool_0_2, pool_2_3], token0, token3),
        CurrencyAmount.fromRawAmount(token0, 100),
        TradeType.EXACT_INPUT
      )

      const { calldata, value } = SwapRouter.swapCallParameters([trade1, trade2], {
        slippageTolerance,
        recipient,
        deadline,
      })

      expect(calldata).toBe(
        '0xac9650d800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000104414bf3890000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000009c40000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007b000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000144c04b8d59000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007b0000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000005f000000000000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000010009c400000000000000000000000000000000000000030009c4000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
      )
      expect(value).toBe('0x00')
    })

    it('two multi-hop exact input', async () => {
      const trade1 = await Trade.fromRoute(
        new Route([pool_0_1, pool_1_3], token0, token3),
        CurrencyAmount.fromRawAmount(token0, 100),
        TradeType.EXACT_INPUT
      )

      const trade2 = await Trade.fromRoute(
        new Route([pool_0_2, pool_2_3], token0, token3),
        CurrencyAmount.fromRawAmount(token0, 100),
        TradeType.EXACT_INPUT
      )

      const { calldata, value } = SwapRouter.swapCallParameters([trade1, trade2], {
        slippageTolerance,
        recipient,
        deadline,
      })

      expect(calldata).toBe(
        '0xac9650d800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000144c04b8d59000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007b0000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000005f000000000000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000010009c400000000000000000000000000000000000000020009c40000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000144c04b8d59000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007b0000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000005f000000000000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000010009c400000000000000000000000000000000000000030009c4000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
      )
      expect(value).toBe('0x00')
    })

    it('ETH in exact input', async () => {
      const trade1 = await Trade.fromRoute(
        new Route([pool_1_weth, pool_1_3], ETHER, token3),
        CurrencyAmount.fromRawAmount(Native.onChain(1), 100),
        TradeType.EXACT_INPUT
      )

      const trade2 = await Trade.fromRoute(
        new Route([pool_3_weth], ETHER, token3),
        CurrencyAmount.fromRawAmount(Native.onChain(1), 100),
        TradeType.EXACT_INPUT
      )

      const { calldata, value } = SwapRouter.swapCallParameters([trade1, trade2], {
        slippageTolerance,
        recipient,
        deadline,
      })

      expect(calldata).toBe(
        '0xac9650d800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000144c04b8d59000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007b0000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000005f0000000000000000000000000000000000000000000000000000000000000042c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20009c400000000000000000000000000000000000000020009c40000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000104414bf389000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000009c40000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007b00000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000061000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
      )
      expect(value).toBe('0xc8')
    })

    it('ETH in exact output', async () => {
      const trade1 = await Trade.fromRoute(
        new Route([pool_1_weth, pool_1_3], ETHER, token3),
        CurrencyAmount.fromRawAmount(token3, 100),
        TradeType.EXACT_OUTPUT
      )

      const trade2 = await Trade.fromRoute(
        new Route([pool_3_weth], ETHER, token3),
        CurrencyAmount.fromRawAmount(token3, 100),
        TradeType.EXACT_OUTPUT
      )

      const { calldata, value } = SwapRouter.swapCallParameters([trade1, trade2], {
        slippageTolerance,
        recipient,
        deadline,
      })

      expect(calldata).toBe(
        '0xac9650d800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000000000144f28c0498000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007b00000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000069000000000000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000040009c400000000000000000000000000000000000000020009c4c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000104db3e2198000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000009c40000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007b00000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000067000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000412210e8a00000000000000000000000000000000000000000000000000000000'
      )
      expect(value).toBe('0xd0')
    })

    it('ETH out exact input', async () => {
      const trade1 = await Trade.fromRoute(
        new Route([pool_1_3, pool_1_weth], token3, ETHER),
        CurrencyAmount.fromRawAmount(token3, 100),
        TradeType.EXACT_INPUT
      )

      const trade2 = await Trade.fromRoute(
        new Route([pool_3_weth], token3, ETHER),
        CurrencyAmount.fromRawAmount(token3, 100),
        TradeType.EXACT_INPUT
      )

      const { calldata, value } = SwapRouter.swapCallParameters([trade1, trade2], {
        slippageTolerance,
        recipient,
        deadline,
      })

      expect(calldata).toBe(
        '0xac9650d800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000000000144c04b8d59000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b0000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000005f000000000000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000040009c400000000000000000000000000000000000000020009c4c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000104414bf3890000000000000000000000000000000000000000000000000000000000000004000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000000000000000000000000000000000000000009c40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b00000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000061000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004449404b7c00000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000'
      )
      expect(value).toBe('0x00')
    })

    it('ETH out exact output', async () => {
      const trade1 = await Trade.fromRoute(
        new Route([pool_1_3, pool_1_weth], token3, ETHER),
        CurrencyAmount.fromRawAmount(Native.onChain(1), 100),
        TradeType.EXACT_OUTPUT
      )

      const trade2 = await Trade.fromRoute(
        new Route([pool_3_weth], token3, ETHER),
        CurrencyAmount.fromRawAmount(Native.onChain(1), 100),
        TradeType.EXACT_OUTPUT
      )

      const { calldata, value } = SwapRouter.swapCallParameters([trade1, trade2], {
        slippageTolerance,
        recipient,
        deadline,
      })

      expect(calldata).toBe(
        '0xac9650d800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000000000144f28c0498000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000690000000000000000000000000000000000000000000000000000000000000042c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20009c400000000000000000000000000000000000000020009c40000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000104db3e21980000000000000000000000000000000000000000000000000000000000000004000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000000000000000000000000000000000000000009c40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b00000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000067000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004449404b7c00000000000000000000000000000000000000000000000000000000000000c8000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000'
      )
      expect(value).toBe('0x00')
    })

    it('two single-hop exact output', async () => {
      const trade1 = await Trade.fromRoute(
        new Route([pool_0_1], token0, token1),
        CurrencyAmount.fromRawAmount(token1, 100),
        TradeType.EXACT_OUTPUT
      )

      const trade2 = await Trade.fromRoute(
        new Route([pool_0_1], token0, token1),
        CurrencyAmount.fromRawAmount(token1, 100),
        TradeType.EXACT_OUTPUT
      )

      const { calldata, value } = SwapRouter.swapCallParameters([trade1, trade2], {
        slippageTolerance,
        recipient,
        deadline,
      })

      expect(calldata).toBe(
        '0xac9650d800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000104db3e21980000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000009c40000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007b000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000104db3e21980000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000009c40000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007b00000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000067000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
      )
      expect(value).toBe('0x00')
    })

    it('one single-hop one multi-hop exact output', async () => {
      const trade1 = await Trade.fromRoute(
        new Route([pool_0_3], token0, token3),
        CurrencyAmount.fromRawAmount(token3, 100),
        TradeType.EXACT_OUTPUT
      )

      const trade2 = await Trade.fromRoute(
        new Route([pool_0_2, pool_2_3], token0, token3),
        CurrencyAmount.fromRawAmount(token3, 100),
        TradeType.EXACT_OUTPUT
      )

      const { calldata, value } = SwapRouter.swapCallParameters([trade1, trade2], {
        slippageTolerance,
        recipient,
        deadline,
      })

      expect(calldata).toBe(
        '0xac9650d800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000104db3e21980000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000009c40000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007b000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000144f28c0498000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007b00000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000069000000000000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000040009c400000000000000000000000000000000000000030009c4000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
      )
      expect(value).toBe('0x00')
    })

    it('two multi-hop exact output', async () => {
      const trade1 = await Trade.fromRoute(
        new Route([pool_0_1, pool_1_3], token0, token3),
        CurrencyAmount.fromRawAmount(token3, 100),
        TradeType.EXACT_OUTPUT
      )

      const trade2 = await Trade.fromRoute(
        new Route([pool_0_2, pool_2_3], token0, token3),
        CurrencyAmount.fromRawAmount(token3, 100),
        TradeType.EXACT_OUTPUT
      )

      const { calldata, value } = SwapRouter.swapCallParameters([trade1, trade2], {
        slippageTolerance,
        recipient,
        deadline,
      })

      expect(calldata).toBe(
        '0xac9650d800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000144f28c0498000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007b00000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000069000000000000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000040009c400000000000000000000000000000000000000020009c40000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000144f28c0498000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007b00000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000069000000000000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000040009c400000000000000000000000000000000000000030009c4000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
      )
      expect(value).toBe('0x00')
    })

    it('different token in fails ', async () => {
      const trade1 = await Trade.fromRoute(
        new Route([pool_2_3], token2, token3),
        CurrencyAmount.fromRawAmount(token2, 100),
        TradeType.EXACT_INPUT
      )

      const trade2 = await Trade.fromRoute(
        new Route([pool_0_1], token0, token1),
        CurrencyAmount.fromRawAmount(token0, 100),
        TradeType.EXACT_INPUT
      )

      expect(() =>
        SwapRouter.swapCallParameters([trade1, trade2], {
          slippageTolerance,
          recipient,
          deadline,
        })
      ).toThrow('TOKEN_IN_DIFF')
    })

    it('different token out fails ', async () => {
      const trade1 = await Trade.fromRoute(
        new Route([pool_0_3], token0, token3),
        CurrencyAmount.fromRawAmount(token0, 100),
        TradeType.EXACT_INPUT
      )

      const trade2 = await Trade.fromRoute(
        new Route([pool_0_1, pool_1_weth], token0, WETH),
        CurrencyAmount.fromRawAmount(token0, 100),
        TradeType.EXACT_INPUT
      )

      expect(() =>
        SwapRouter.swapCallParameters([trade1, trade2], {
          slippageTolerance,
          recipient,
          deadline,
        })
      ).toThrow('TOKEN_OUT_DIFF')
    })

    it('sqrtPriceLimitX96', async () => {
      const trade1 = await Trade.fromRoute(
        new Route([pool_0_1], token0, token1),
        CurrencyAmount.fromRawAmount(token0, 100),
        TradeType.EXACT_INPUT
      )

      const trade2 = await Trade.fromRoute(
        new Route([pool_0_1], token0, token1),
        CurrencyAmount.fromRawAmount(token0, 100),
        TradeType.EXACT_INPUT
      )
      const { calldata, value } = SwapRouter.swapCallParameters([trade1, trade2], {
        slippageTolerance,
        recipient,
        deadline,
        sqrtPriceLimitX96: 2n ** 128n,
      })

      expect(calldata).toBe(
        '0xac9650d800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000104414bf3890000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000009c40000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007b000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000610000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000104414bf3890000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000009c40000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007b00000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000061000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
      )
      expect(value).toBe('0x00')
    })

    it('fee with eth out', async () => {
      const trade1 = await Trade.fromRoute(
        new Route([pool_1_3, pool_1_weth], token3, ETHER),
        CurrencyAmount.fromRawAmount(token3, 100),
        TradeType.EXACT_INPUT
      )

      const trade2 = await Trade.fromRoute(
        new Route([pool_3_weth], token3, ETHER),
        CurrencyAmount.fromRawAmount(token3, 100),
        TradeType.EXACT_INPUT
      )

      const { calldata, value } = SwapRouter.swapCallParameters([trade1, trade2], {
        slippageTolerance,
        recipient,
        deadline,
        fee: {
          fee: new Percent(5, 1000),
          recipient,
        },
      })

      expect(calldata).toBe(
        '0xac9650d800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000000000144c04b8d59000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b0000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000005f000000000000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000040009c400000000000000000000000000000000000000020009c4c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000104414bf3890000000000000000000000000000000000000000000000000000000000000004000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000000000000000000000000000000000000000009c40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b0000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000006100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000849b2c0a3700000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000'
      )
      expect(value).toBe('0x00')
    })

    it('fee with eth in using exact output', async () => {
      const trade1 = await Trade.fromRoute(
        new Route([pool_1_weth, pool_1_3], ETHER, token3),
        CurrencyAmount.fromRawAmount(token3, 100),
        TradeType.EXACT_OUTPUT
      )

      const trade2 = await Trade.fromRoute(
        new Route([pool_3_weth], ETHER, token3),
        CurrencyAmount.fromRawAmount(token3, 100),
        TradeType.EXACT_OUTPUT
      )

      const { calldata, value } = SwapRouter.swapCallParameters([trade1, trade2], {
        slippageTolerance,
        recipient,
        deadline,
        fee: {
          fee: new Percent(5, 1000),
          recipient,
        },
      })

      expect(calldata).toBe(
        '0xac9650d80000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000000000000000000000000000144f28c0498000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b00000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000069000000000000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000040009c400000000000000000000000000000000000000020009c4c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000104db3e2198000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000009c40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b0000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000006700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a4e0e189a0000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000c800000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000412210e8a00000000000000000000000000000000000000000000000000000000'
      )
      expect(value).toBe('0xd0')
    })

    it('fee', async () => {
      const trade1 = await Trade.fromRoute(
        new Route([pool_0_1, pool_1_3], token0, token3),
        CurrencyAmount.fromRawAmount(token0, 100),
        TradeType.EXACT_INPUT
      )

      const trade2 = await Trade.fromRoute(
        new Route([pool_0_2, pool_2_3], token0, token3),
        CurrencyAmount.fromRawAmount(token0, 100),
        TradeType.EXACT_INPUT
      )

      const { calldata, value } = SwapRouter.swapCallParameters([trade1, trade2], {
        slippageTolerance,
        recipient,
        deadline,
        fee: {
          fee: new Percent(5, 1000),
          recipient,
        },
      })

      expect(calldata).toBe(
        '0xac9650d800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000144c04b8d59000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b0000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000005f000000000000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000010009c400000000000000000000000000000000000000020009c40000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000144c04b8d59000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b0000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000005f000000000000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000010009c400000000000000000000000000000000000000030009c400000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a4e0e189a0000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000be00000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000'
      )
      expect(value).toBe('0x00')
    })
  })

  describe('trade with multiple routes', () => {
    it('one single-hop one multi-hop exact input', async () => {
      const trade = await Trade.fromRoutes<Token, Token, TradeType.EXACT_INPUT>(
        [
          { amount: CurrencyAmount.fromRawAmount(token0, 100), route: new Route([pool_0_3], token0, token3) },
          { amount: CurrencyAmount.fromRawAmount(token0, 100), route: new Route([pool_0_2, pool_2_3], token0, token3) },
        ],
        TradeType.EXACT_INPUT
      )

      const { calldata, value } = SwapRouter.swapCallParameters([trade], {
        slippageTolerance,
        recipient,
        deadline,
      })

      expect(calldata).toBe(
        '0xac9650d800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000104414bf3890000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000009c40000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007b000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000144c04b8d59000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007b0000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000005f000000000000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000010009c400000000000000000000000000000000000000030009c4000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
      )
      expect(value).toBe('0x00')
    })

    it('two multi-hop exact input', async () => {
      const trade = await Trade.fromRoutes<Token, Token, TradeType.EXACT_INPUT>(
        [
          { amount: CurrencyAmount.fromRawAmount(token0, 100), route: new Route([pool_0_1, pool_1_3], token0, token3) },
          { amount: CurrencyAmount.fromRawAmount(token0, 100), route: new Route([pool_0_2, pool_2_3], token0, token3) },
        ],
        TradeType.EXACT_INPUT
      )

      const { calldata, value } = SwapRouter.swapCallParameters([trade], {
        slippageTolerance,
        recipient,
        deadline,
      })

      expect(calldata).toBe(
        '0xac9650d800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000144c04b8d59000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007b0000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000005f000000000000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000010009c400000000000000000000000000000000000000020009c40000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000144c04b8d59000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007b0000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000005f000000000000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000010009c400000000000000000000000000000000000000030009c4000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
      )
      expect(value).toBe('0x00')
    })

    it('ETH in exact input', async () => {
      const trade = await Trade.fromRoutes<Ether, Token, TradeType.EXACT_INPUT>(
        [
          {
            amount: CurrencyAmount.fromRawAmount(Native.onChain(1), 100),
            route: new Route([pool_1_weth, pool_1_3], ETHER, token3),
          },
          {
            amount: CurrencyAmount.fromRawAmount(Native.onChain(1), 100),
            route: new Route([pool_3_weth], ETHER, token3),
          },
        ],
        TradeType.EXACT_INPUT
      )

      const { calldata, value } = SwapRouter.swapCallParameters([trade], {
        slippageTolerance,
        recipient,
        deadline,
      })

      expect(calldata).toBe(
        '0xac9650d800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000144c04b8d59000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007b0000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000005f0000000000000000000000000000000000000000000000000000000000000042c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20009c400000000000000000000000000000000000000020009c40000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000104414bf389000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000009c40000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007b00000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000061000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
      )
      expect(value).toBe('0xc8')
    })

    it('ETH in exact output', async () => {
      const trade = await Trade.fromRoutes<Ether, Token, TradeType.EXACT_OUTPUT>(
        [
          {
            amount: CurrencyAmount.fromRawAmount(token3, 100),
            route: new Route([pool_1_weth, pool_1_3], ETHER, token3),
          },
          { amount: CurrencyAmount.fromRawAmount(token3, 100), route: new Route([pool_3_weth], ETHER, token3) },
        ],
        TradeType.EXACT_OUTPUT
      )

      const { calldata, value } = SwapRouter.swapCallParameters([trade], {
        slippageTolerance,
        recipient,
        deadline,
      })

      expect(calldata).toBe(
        '0xac9650d800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000000000144f28c0498000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007b00000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000069000000000000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000040009c400000000000000000000000000000000000000020009c4c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000104db3e2198000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000009c40000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007b00000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000067000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000412210e8a00000000000000000000000000000000000000000000000000000000'
      )
      expect(value).toBe('0xd0')
    })

    it('ETH out exact input', async () => {
      const trade = await Trade.fromRoutes<Token, Ether, TradeType.EXACT_INPUT>(
        [
          {
            amount: CurrencyAmount.fromRawAmount(token3, 100),
            route: new Route([pool_1_3, pool_1_weth], token3, ETHER),
          },
          {
            amount: CurrencyAmount.fromRawAmount(token3, 100),
            route: new Route([pool_3_weth], token3, ETHER),
          },
        ],
        TradeType.EXACT_INPUT
      )

      const { calldata, value } = SwapRouter.swapCallParameters([trade], {
        slippageTolerance,
        recipient,
        deadline,
      })

      expect(calldata).toBe(
        '0xac9650d800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000000000144c04b8d59000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b0000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000005f000000000000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000040009c400000000000000000000000000000000000000020009c4c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000104414bf3890000000000000000000000000000000000000000000000000000000000000004000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000000000000000000000000000000000000000009c40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b00000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000061000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004449404b7c00000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000'
      )
      expect(value).toBe('0x00')
    })

    it('ETH out exact output', async () => {
      const trade = await Trade.fromRoutes<Token, Ether, TradeType.EXACT_OUTPUT>(
        [
          {
            amount: CurrencyAmount.fromRawAmount(Native.onChain(1), 100),
            route: new Route([pool_1_3, pool_1_weth], token3, ETHER),
          },
          {
            amount: CurrencyAmount.fromRawAmount(Native.onChain(1), 100),
            route: new Route([pool_3_weth], token3, ETHER),
          },
        ],
        TradeType.EXACT_OUTPUT
      )

      const { calldata, value } = SwapRouter.swapCallParameters([trade], {
        slippageTolerance,
        recipient,
        deadline,
      })

      expect(calldata).toBe(
        '0xac9650d800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000000000144f28c0498000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000690000000000000000000000000000000000000000000000000000000000000042c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20009c400000000000000000000000000000000000000020009c40000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000104db3e21980000000000000000000000000000000000000000000000000000000000000004000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000000000000000000000000000000000000000009c40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b00000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000067000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004449404b7c00000000000000000000000000000000000000000000000000000000000000c8000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000'
      )
      expect(value).toBe('0x00')
    })

    it('one single-hop one multi-hop exact output', async () => {
      const trade = await Trade.fromRoutes<Token, Token, TradeType.EXACT_OUTPUT>(
        [
          { amount: CurrencyAmount.fromRawAmount(token3, 100), route: new Route([pool_0_3], token0, token3) },
          { amount: CurrencyAmount.fromRawAmount(token3, 100), route: new Route([pool_0_2, pool_2_3], token0, token3) },
        ],
        TradeType.EXACT_OUTPUT
      )

      const { calldata, value } = SwapRouter.swapCallParameters([trade], {
        slippageTolerance,
        recipient,
        deadline,
      })

      expect(calldata).toBe(
        '0xac9650d800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000104db3e21980000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000009c40000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007b000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000144f28c0498000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007b00000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000069000000000000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000040009c400000000000000000000000000000000000000030009c4000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
      )
      expect(value).toBe('0x00')
    })

    it('two multi-hop exact output', async () => {
      const trade = await Trade.fromRoutes<Token, Token, TradeType.EXACT_OUTPUT>(
        [
          { amount: CurrencyAmount.fromRawAmount(token3, 100), route: new Route([pool_0_1, pool_1_3], token0, token3) },
          { amount: CurrencyAmount.fromRawAmount(token3, 100), route: new Route([pool_0_2, pool_2_3], token0, token3) },
        ],
        TradeType.EXACT_OUTPUT
      )

      const { calldata, value } = SwapRouter.swapCallParameters([trade], {
        slippageTolerance,
        recipient,
        deadline,
      })

      expect(calldata).toBe(
        '0xac9650d800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000144f28c0498000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007b00000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000069000000000000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000040009c400000000000000000000000000000000000000020009c40000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000144f28c0498000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007b00000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000069000000000000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000040009c400000000000000000000000000000000000000030009c4000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
      )
      expect(value).toBe('0x00')
    })
  })
})
